package rs.udd.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.envers.Audited;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import rs.udd.model.Role.Roles;

@Audited
@Entity
@Table(name = "UDD_AGENTS")
public class Agent extends AbstractUser implements UserDetails {

	private static final long serialVersionUID = -2630346165145041861L;

	public enum Gender {
		MALE,
		FEMALE
	}

	@Past
	@NotNull
	private Date birthDate;

	@NotNull
	@Enumerated(EnumType.STRING)
	private Gender gender;

	private String folderWithCv;

	@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, targetEntity = Advertiser.class) // initiates hiding of Advertisers
	@JoinTable(name = "UDD_HIDDEN_ADVERTISERS",
			joinColumns = { @JoinColumn(name = "fkAgent", referencedColumnName = "id", nullable = false) },
			inverseJoinColumns = { @JoinColumn(name = "fkAdvertiser", referencedColumnName = "id", nullable = false) })
	private Set<Advertiser> hiddenAdvertisers = new HashSet<Advertiser>();

	@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, targetEntity = Category.class) // initiates hiding of Categories
	@JoinTable(name = "UDD_HIDDEN_CATEGORIES",
			joinColumns = { @JoinColumn(name = "fkAgent", referencedColumnName = "id", nullable = false) },
			inverseJoinColumns = { @JoinColumn(name = "fkCategory", referencedColumnName = "id", nullable = false) })
	private Set<Category> hiddenCategories = new HashSet<Category>();

	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "agent")
	private Set<AgentAdvertApplication> applicationsForAdvert = new HashSet<AgentAdvertApplication>();

	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "agent")
	private Set<AdvertiserAgentContact> contactedByAdvertiser = new HashSet<AdvertiserAgentContact>();

	private String indexedDocumentId;

	public Agent() {

	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {

		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(1);
		SimpleGrantedAuthority authority = new SimpleGrantedAuthority(Roles.AGENT.name());
		authorities.add(authority);

		return authorities;
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return getActive();
	}

	@Override
	public String getUsername() {
		return super.getUsername();
	}

	@Override
	public String getPassword() {
		return super.getPassword();
	}

	public Date getBirthDate() {
		return birthDate;
	}

	public void setBirthDate(Date birthDate) {
		this.birthDate = birthDate;
	}

	public Gender getGender() {
		return gender;
	}

	public void setGender(Gender gender) {
		this.gender = gender;
	}

	public String getFolderWithCv() {
		return folderWithCv;
	}

	public void setFolderWithCv(String folderWithCv) {
		this.folderWithCv = folderWithCv;
	}

	public Set<Advertiser> getHiddenAdvertisers() {
		return hiddenAdvertisers;
	}

	public void setHiddenAdvertisers(Set<Advertiser> hiddenAdvertisers) {
		this.hiddenAdvertisers = hiddenAdvertisers;
	}

	public Set<Category> getHiddenCategories() {
		return hiddenCategories;
	}

	public void setHiddenCategories(Set<Category> hiddenCategories) {
		this.hiddenCategories = hiddenCategories;
	}

	public String getIndexedDocumentId() {
		return indexedDocumentId;
	}

	public void setIndexedDocumentId(String indexedDocumentId) {
		this.indexedDocumentId = indexedDocumentId;
	}
	
	public Set<AgentAdvertApplication> getApplicationsForAdvert() {
		return applicationsForAdvert;
	}

	public void setApplicationsForAdvert(Set<AgentAdvertApplication> applicationsForAdvert) {
		this.applicationsForAdvert = applicationsForAdvert;
	}

	public Set<AdvertiserAgentContact> getContactedByAdvertiser() {
		return contactedByAdvertiser;
	}

	public void setContactedByAdvertiser(Set<AdvertiserAgentContact> contactedByAdvertiser) {
		this.contactedByAdvertiser = contactedByAdvertiser;
	}
	
	@Override
	public int hashCode() {

		return new HashCodeBuilder()
				.appendSuper(super.hashCode())
				.append(birthDate)
				.append(gender)
				.append(folderWithCv)
				.hashCode();
	}

	@Override
	public boolean equals(Object obj) {

		if (obj == null) {
			return false;
		}
		if (obj == this) {
			return true;
		}
		if (obj.getClass() != getClass()) {
			return false;
		}

		final Agent otherAgent = (Agent) obj;

		return new EqualsBuilder()
				.appendSuper(super.equals(obj))
				.append(birthDate, otherAgent.getBirthDate())
				.append(gender, otherAgent.getGender())
				.append(folderWithCv, otherAgent.getFolderWithCv())
				.isEquals();
	}

	@Override
	public String toString() {

		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
				.append(super.toString())
				.append("birthDate", birthDate)
				.append("gender", gender)
				.append("folderWithCv", folderWithCv)
				.toString();
	}

}
